#ifndef MEMPOOL_T_H_
#define MEMPOOL_T_H_
#include "memPage_t.h"
#include <string.h>
#include <math.h>
#include <list>
#include <iostream>
using namespace std;

class memPool_t {
public:
	memPool_t(); //Default constructor
	memPool_t(int numPages);

	virtual ~memPool_t(); // Destructor

	int getCurrentPos() const {
		return m_curPos;
	}

	bool isEmpty() const {
		return (m_actualSize == 0);
	}

	int getActualSize() const {
		return m_actualSize;
	}

	int getCapacity() const {
		return m_capacity;
	}

	int getNumOfPages() const {
		return m_memPages.size();
	}

	memPage_t* getFirstPage() const {
		return *(m_memPages.begin());
	}
	memPage_t* getLastPage() const {
		return *(m_memPages.rbegin());
	}
	memPage_t* getCurrentPage() const {
		return m_curPage;
	}

	bool setCurrentPos(int newPos);
	void createNewPages(int numPages);

	template<class T> bool write(const T& obj, int amount) {
		return write(obj, amount, m_curPos);
	}

	template<class T> bool write(const T& obj, int amount, int pos) {
		if ((amount < 0) || (pos < 0) || (pos > m_actualSize)) {
			return false;
		}
		if (pos + amount + 1 > m_capacity) {
			createNewPages(ceil((float)((pos + amount + 1) - m_capacity) / (float)getDefaultPageSize()));
		}

		int tempCurPos;
		memPage_t* tempCurPage = getPageByPosition(pos, tempCurPos);
		if (!tempCurPage->setCurrentPos(tempCurPos)) {
			return false;
		}

		int availSize = 0;
		int amountToWrite = 0;
		int bytesWritten = 0;
		int bytesLeft = amount;
		while (bytesLeft > 0) {
			bytesWritten = amount - bytesLeft;
			availSize = tempCurPage->getCapacity() - tempCurPage->getCurrentPos();
			if (availSize == 0) {
				tempCurPage = getPageByPosition(pos + bytesWritten, tempCurPos);
				if (!tempCurPage->setCurrentPos(tempCurPos)) {
					return false;
				}
				availSize = tempCurPage->getCapacity() - tempCurPage->getCurrentPos();
			}
			amountToWrite = bytesLeft < availSize ? bytesLeft : availSize;

			if (!tempCurPage->write(*((byte*)&obj + bytesWritten), amountToWrite)) {
				return false;
			}

			tempCurPos =+ amountToWrite;
			bytesLeft -= amountToWrite;
		}

		setCurrentPos(pos + amount);
		m_actualSize = (m_curPos > m_actualSize) ? m_curPos : m_actualSize;

		return true;
	}

	template<class T> bool read(T& obj, int amount) {
		return read(obj, amount, m_curPos);
	}

	template<class T> bool read(T& obj, int amount, int pos) {
		if ((amount < 0) || (pos < 0) || (pos + amount > m_actualSize)) {
			return false;
		}
		int tempCurPos;
		memPage_t* tempCurPage = getPageByPosition(pos, tempCurPos);
		if (!tempCurPage->setCurrentPos(tempCurPos)) {
			return false;
		}

		int availSize = 0;
		int amountToRead = 0;
		int bytesRead = 0;
		int bytesLeft = amount;
		while (bytesLeft > 0) {
			bytesRead = amount - bytesLeft;
			availSize = tempCurPage->getCapacity() - tempCurPage->getCurrentPos();
			if (availSize == 0) {
				int tempCurPos;
				tempCurPage = getPageByPosition(pos + bytesRead, tempCurPos);
				if (!tempCurPage->setCurrentPos(tempCurPos)) {
					return false;
				}
				availSize = tempCurPage->getCapacity() - tempCurPage->getCurrentPos();
			}
			amountToRead = bytesLeft < availSize ? bytesLeft : availSize;


			if (!tempCurPage->read(*((byte*)&obj + bytesRead), amountToRead)) {
				return false;
			}

			tempCurPos =+ amountToRead;
			bytesLeft -= amountToRead;
		}

		setCurrentPos(pos + amount);

		return true;
	}

	static void setDefaultPageSize(int pageSize) {
		memPage_t::setDefaultPageSize(pageSize);
	}

	static int getDefaultPageSize() {
		return memPage_t::getDefaultPageSize();
	}

private:
	list<memPage_t*> m_memPages;
	int m_curPos;
	int m_actualSize;
	int m_capacity;
	memPage_t* m_curPage;

	// Don't allow copy of object
	memPool_t(const memPool_t& mp); // Copy constructor
	const memPool_t& operator=(const memPool_t& mp); //Assignment operator

	void constructPool(int numPages);
	memPage_t* getPageByPosition(int globalPos, int& pagePos) const;
};
#endif /*MEMPOOL_T_H_*/
